#!/usr/bin/python

import sys;
import ply.lex as lex;
import ply.yacc as yacc;

variables = {} # globalna tablica do wyrazen arytmetycznych
line = 0 # globalny numer linii
i = 1  #numer kolejnej zmiennej mem<i>


class Node:
    left , right, value = None, None, 0
    def __init__(self, left, right, value):
        self.left = left
        self.right = right
        self.value = value


class NodeArithm(Node):
    '''
    Wyrazenie arytmetyczne
    '''
    def __init__(self, left, right, value):
        global i
        super(NodeArithm, self).__init__(left, right, value)
        self.var_name = 'mem'+str(i)
        i = i+1
    def eval(self):
        global line
        # dodanie wpisu do tablicy
        # operator | arg1 | arg2 | nazwa_zmiennej_tymczasowej memX
        variables[self.var_name] = (self.value, self.left.var_name, self.right.var_name)

        if type(self.left) != NodeConstant:
            le = '%s\n' % self.left.eval()
        else:
            le = ''
            
        if type(self.right) != NodeConstant:
            ri = '%s\n' % self.right.eval()
        else:
            ri = ''    
        line+=1
        self.code = '%s%s%i: %s = %s %s %s' % (le, ri, line, self.var_name, variables[self.var_name][1], variables[self.var_name][0], variables[self.var_name][2])
        return self.code;



class NodeAssign(Node):
    '''
    Instrukcja przypisania
    '''
    def __init__(self, left, right):
        super(NodeAssign, self).__init__(left, right, '=')

    def eval(self):
        global line
        # = | wartosc | | zmienna
        variables[self.left] = ('=', self.right.var_name, '')

        if type(self.right) != NodeConstant:
            ri = '%s\n' % self.right.eval()
        else:
            ri = ''  

        line+=1
                    
        self.code = '%s%i: %s = %s\n' % (ri, line, self.left, variables[self.left][1])
        
        return self.code;



class NodeInstructions(Node):
    '''
    Zestaw instrukcji wszystkich typow
    '''
    def __init__(self, left, right):
        super(NodeInstructions, self).__init__(left, right, 'instructions')
    def eval(self):
        if self.right != None:
            return '%s%s' % (self.left.eval(), self.right.eval())
        else:
            return '%s' % (self.left.eval())


class NodeConstant(Node):
    '''
    Stala - znakowa lub liczbowa
    '''
    def __init__(self, value):
        super(NodeConstant, self).__init__(None, None, value)
        try:
            ival = int(self.value)
        except (ValueError):
            ival = self.value
        self.var_name = str(ival)
    def eval(self):
        return str(self.value)



class NodeCondition(Node):
    '''
    Warunek dzialania w instrukcji if(warunek)
    '''
    def __init__(self, left, right, value):
        super(NodeCondition, self).__init__(left, right, value)
    def eval(self):
        global line
        #odwrocenie warunku
        if self.value == '<':
            self.newval = '>='
        elif self.value == '>':
            self.newval = '<='
        elif self.value == '>=':
            self.newval = '<'
        elif self.value == '<=':
            self.newval = '>'
        elif self.value == '==':
            self.newval = '!='
        elif self.value == '!=':
            self.newval = '=='
            
        if type(self.left) != NodeConstant:
            le = '%s\n' % self.left.eval()
        else:
            le = ''
            
        if type(self.right) != NodeConstant:
            ri = '%s\n' % self.right.eval()
        else:
            ri = ''
        return '%s%s' % (le, ri)


class NodeIf(Node):
    '''wyrazenie warunkowe'''
    def __init__(self, left, right):
        super(NodeIf, self).__init__(left, right, None)
    def eval(self):
        myline = line+1                                 #zapamietanie nastepnego numeru linii
        lefteval = self.left.eval()                     # tu obliczono juz numery linii
        lengthOfConds = lefteval.count('\n')            # dlugosc ewaluowanego bloku zmiennych warunkowych w instrukcjach kodu czworkowego
        righteval = self.right.eval()                   # tu takze obliczono numery linii
        #if<wyrazenie> zmienna_po_lewej zmienna_po_prawej, skok   //skok = aktualna linia + dlugosc bloku wyliczajacego zmienne
        # + dlugosc bloku if w instrukcjach czworkowych + 2 (biezaca instrukcja i jeszcze jedna, bo chcemy przekroczyc blok if
        wyrazenie = 'if%s %s %s %i' % (self.left.newval, self.left.left.var_name, self.left.right.var_name, myline+lengthOfConds+self.right.lengthOfIf+2)
        return '%s%i: %s\n%s' % (lefteval, myline+lengthOfConds, wyrazenie, righteval)



class NodeElse(Node):
    '''Wyrazenie else'''
    def __init__(self, left, right):
        super(NodeElse, self).__init__(left, right, None)
    def eval(self):
        global line
        line+=1
        if(self.right != None):  #jesli za blokiem if jest blok else
            
            #lefteval - stmt zaraz za ifem
            lefteval = self.left.eval()
            self.lengthOfIf = lefteval.count('\n')
            line +=1
            #righteval - stmt zaraz za else
            righteval = self.right.eval()
            self.lengthOfElse = righteval.count('\n')
            
            #blok_if, linia-dlugosc_bloku_else, skok_bezwarunkowy, numer_linii+1 (bo dodajemy jeszcze siebie) i na koncu blok else
            s = '%s%i: %s %i' % (lefteval, line-self.lengthOfElse, 'if== 1 1', line+1)
            return '%s\n%s' % (s, righteval)
            
        else:
            lefteval = self.left.eval()
            self.lengthOfIf = lefteval.count('\n')-1 # dlugosc bloku if 
            print(self.lengthOfIf)
            s = '%s' % (lefteval)
            return '%s' % (s)


literals = "{}()<>=;+-*/"

tokens = ( "ID", "NUMBER", "IF",  "ELSE", "EQ", "NEQ", "LE", "GE" );

t_ignore = ' \t'



def t_newline(t):
    r'\n+'
    t.lexer.lineno += len(t.value)


def t_error(t):
    print("Illegal character %s" % t.value[0])
    t.lexer.skip(1)



def t_NUMBER(t):
    r"\d+(\.\d*)?|\.\d+"
    t.value = float(t.value)
    return t


def t_IF(t):
    "if"
    return t


def t_ELSE(t):
    "else"
    return t


def t_LE(t):
    r"<="
    return t


def t_GE(t):
    r">="
    return t


def t_EQ(t):
    r"=="
    return t


def t_NEQ(t):
    r"!="
    return t


def t_ID(t):
    r"[a-zA-Z_]\w*"
    return t


precedence = (
   ("nonassoc", 'IFX'),
   ("nonassoc", 'ELSE'),
   ("nonassoc", '<', '>', 'EQ', 'NEQ', 'LE', 'GE'),
   ("left", '+', '-'),
   ("left", '*', '/') )



# pamiec zmiennych i memow

def p_error(p):
    print("Syntax error at line %d" %lexer.lineno)


def p_program(p):
    """program : instructions"""
    p[0] = p[1]
    print(p[0].eval())


def p_instructions(p):
    """instructions : instructions instruction"""
    p[0] = NodeInstructions(p[1], p[2])
    
def p_instructions_single(p):
    """instructions : instruction"""
    p[0] = NodeInstructions(p[1], None)


def p_instruction(p):
    """instruction : assignment
                   | choice_instr"""
    p[0]=p[1]

def p_assignment(p):
    """assignment : ID '=' expression ';' """
    p[0] = NodeAssign(p[1],p[3])


    
def p_expression(p):
    """expression : NUMBER
                  | ID
                  """
    p[0] = NodeConstant(p[1])


def p_expression_dz(p):
    """expression : expression '+' expression
                  | expression '-' expression
                  | expression '*' expression
                  | expression '/' expression
    """
    p[0] = NodeArithm(p[1],p[3],p[2])



    
def p_expression_na(p):
    """ expression : '(' expression ')' """
    p[0]=p[2]




def p_choice_instr(p):
    """choice_instr : IF '(' condition ')' stmt %prec IFX
                    | IF '(' condition ')' stmt ELSE stmt """
    if len(p) < 8:
        els = NodeElse(p[5], None)
        p[0] = NodeIf(p[3], els)
    else:
        els = NodeElse(p[5], p[7])
        p[0] = NodeIf(p[3], els)




def p_condition(p):
    """condition : expression EQ  expression
                 | expression NEQ expression
                 | expression GE  expression
                 | expression LE  expression
                 | expression '<' expression
                 | expression '>' expression """
    p[0] = NodeCondition(p[1], p[3], p[2])




def p_stmt(p):
    """stmt : assignment
            | '{' instructions '}'
            | choice_instr """
    if p[1] == '{':
        p[0] = p[2]
    else:
        p[0] = p[1]



if len(sys.argv)>1:
    file = open(sys.argv[1], "r");
else:
    file = open("example.txt", "r");


lexer = lex.lex()
parser = yacc.yacc()
text = file.read()
parser.parse(text, lexer=lexer)
